package com.tools.utils.counter;


import org.apache.log4j.Logger;

import java.text.DecimalFormat;
import java.util.List;
import java.util.Stack;

import static com.tools.utils.counter.OperatorComparator.compareOperator;

/**
 * Calculator
 *
 * @author  < a href=" ">ZhangYang</ a>
 * @version v1.0.01
 * @date    2018-08-22 15:45:07
 */
public class Calculator {

  private static final Logger LOG = Logger.getLogger(Calculator.class);

  public static String calculate(String expression) throws ExpressionIllegalException {

    List<Unit> units = ExpressionParser.parse(expression);
    LOG.error("units.size() equals " + units.size());
    Stack<Unit> operators = new Stack<Unit>();
    Stack<Unit> operands = new Stack<Unit>();

    int index = 0;
    while (index < units.size() || operators.size() != 0) {

      Unit unit = units.get(index);
      if (unit.getType() == Unit.Type.OPERAND) {
        operands.push(unit);
        index += 1;
      } else {
        if (operators.empty() || compareOperator(operators.peek(), unit) == '<') {
          operators.push(unit);
          index += 1;
        } else if (compareOperator(operators.peek(), unit) == '=') {
          Unit top = operators.pop();
          index += 1;
        } else if (compareOperator(operators.peek(), unit) == '>') {
          Unit top = operators.pop();
          if (top.isUnary() && operands.size() >= 1) {
            Unit operand1 = operands.pop();
            Unit res = top.operate(operand1);
            operands.push(res);
          } else if (top.isBinary() && operands.size() >= 2) {
            Unit operand2 = operands.pop();
            Unit operand1 = operands.pop();
            Unit res = top.operate(operand1, operand2);
            operands.push(res);
          } else {
            throw new ExpressionIllegalException();
          }
        } else {
          throw new ExpressionIllegalException();
        }
      }
    }
    if (operands.size() != 1) {
      throw new ExpressionIllegalException();
    }
    Unit finalResult = operands.pop();
    double res = finalResult.getVal();
    if (Double.isNaN(res)) {
      return "NaN";
    } else if (res < Double.parseDouble("1.0E14") || Double.isInfinite(res)) {
      return new DecimalFormat("#.##############").format(res);
    } else {
      return Double.toString(finalResult.getVal());
    }
  }

}
